#include "TimerManager.hpp"
#include "../base/TimeUnit.hpp"
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <vector>

// 局部时钟，用来获取时间
using DBServer::Base::Timer::Time;
static Time l_clock{};

namespace DBServer::coroutine {

bool Timer::Comparator::operator()(const Timer::ptr &lhs, const Timer::ptr &rhs) const {
    if (!lhs && !rhs) {
        return false;
    }
    if (!lhs) {
        return true;
    }
    if (!rhs) {
        return false;
    }
    if (lhs->next < rhs->next) {
        return true;
    }
    if (rhs->next > lhs->next) {
        return false;
    }
    return lhs.get() < rhs.get();
}

bool Timer::cancel() {
    std::unique_lock<std::shared_mutex> rb_lock(manager->rb_mtx);

    auto iter = manager->timer_heap.find(shared_from_this());
    if (iter != manager->timer_heap.end())
    {
        manager->timer_heap.erase(iter);
        return true;
    }
    return false;
}

bool Timer::refresh() {
    l_clock.cur();
    std::unique_lock<std::shared_mutex> rb_lock(manager->rb_mtx);

    auto iter = manager->timer_heap.find(shared_from_this());
    if (iter == manager->timer_heap.end()) {
        return false;
    }
    manager->timer_heap.erase(iter);
    next = l_clock.get_time_ms() + ms;
    manager->timer_heap.insert(shared_from_this());
    return true;
}

bool Timer::reset(std::uint64_t ms_, bool from_now) {
    l_clock.cur();

    std::unique_lock<std::shared_mutex> rb_lock(manager->rb_mtx);

    auto iter = manager->timer_heap.find(shared_from_this());
    if (iter == manager->timer_heap.end()) {
        return false;
    }
    ms = ms_;
    if (from_now) {
        next = l_clock.get_time_ms() + ms;
        manager->timer_heap.erase(iter);
        manager->timer_heap.insert(shared_from_this());
    }
    return true;
}

Timer::Timer(std::uint64_t ms_, std::function<void()> cb_, bool recurring_, TimerManager *manager_) 
:   recurring(recurring_),
    ms(ms_),
    cb(cb_),
    manager(manager_)
{
    l_clock.cur();
    next = l_clock.get_time_ms() + ms;
}

Timer::Timer(std::uint64_t next_) 
:   next(next_)
{}

Timer::ptr TimerManager::add_timer(std::uint64_t ms, std::function<void()> cb, bool recurring) {
    Timer::ptr new_timer{new Timer(ms, cb, recurring, this)};
    rb_mtx.lock();
    add_timer(new_timer, rb_mtx);
    return new_timer;
}

Timer::ptr TimerManager::add_condition_timer(std::uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond, bool recurring) {
    return add_timer(
        ms,
        [&]() { 
            auto ptr = weak_cond.lock();
            if (ptr) {
                cb();
            }
        },
        recurring);
}

std::uint64_t TimerManager::get_next_time() {
    rb_mtx.lock_shared();
    tickled = false;
    if (timer_heap.empty())
    {
        return ~0ull;
    }

    auto next_timer = timer_heap.cbegin();
    l_clock.cur();
    std::uint64_t next_time = (*next_timer)->ms - l_clock.get_time_ms();
    rb_mtx.unlock_shared();
    return next_time >= 0 ? next_time : 0;
}

void TimerManager::list_expire_cb(std::vector<std::function<void()>>& cbs) {
    std::vector<Timer::ptr> expires;

    rb_mtx.lock_shared();
    if (timer_heap.empty()) {
        rb_mtx.unlock_shared();
        return;
    }
    rb_mtx.unlock_shared();

    rb_mtx.lock();

    auto iter = timer_heap.begin();
    l_clock.cur();
    while (iter != timer_heap.end() && (*iter)->next <= l_clock.get_time_ms())
        ++iter;

    expires.insert(expires.begin(), timer_heap.begin(), iter);
    timer_heap.erase(timer_heap.begin(), iter);
    cbs.reserve(expires.size());

    l_clock.cur();
    std::uint64_t cur = l_clock.get_time_ms();
    for (auto &timer : expires)
    {
        cbs.push_back(timer->cb);
        if (timer->recurring)
        {
            timer->next = cur + timer->ms;
            timer_heap.insert(timer);
        }
        else
        {
            timer->cb = nullptr;
        }
    }

    rb_mtx.unlock();
}

bool TimerManager::empty() {
    rb_mtx.lock_shared();
    bool ret = timer_heap.empty();
    rb_mtx.unlock_shared();
    return ret;
}

void TimerManager::add_timer(Timer::ptr val, std::shared_mutex &mtx) {
    timer_heap.insert(val);
    bool tick = (val.get() == timer_heap.begin()->get()) && !tickled;
    if (tick) {
        tickled = true;
    }

    mtx.unlock();

    if (tick) {
        on_timer_insert_at_front();
    }
}

}